package org.base.common.interceptor;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.base.common.annotation.RequiredPerm;
import org.base.common.exception.BusinessException;
import org.base.common.exception.ExceptionEnum;
import org.base.common.token.JWTToken;
import org.base.common.util.SpringContextUtils;
import org.base.common.util.TokenUtils;
import org.base.common.util.UserUtils;
import org.base.sys.constant.SysConstant;
import org.base.sys.service.SysUrlPermissionService;
import org.base.sys.service.SysUserService;
import org.springframework.lang.Nullable;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
public class AuthInterceptor implements HandlerInterceptor {


    /**
     * token
     */
    private static final String TOKEN = "token";

    /**
     * 拦截范围
     */
    private static final String INTERCEPT_PACKAGE_PREFIX = "org";

    private Map<String, RequestMappingInfo> requestMappingInfoMap = new ConcurrentHashMap<>(100);

    /**
     * 在请求执行前执行的
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 放过静态资源
        if (handler instanceof ResourceHttpRequestHandler) {
            return true;
        }
        String token = request.getHeader(TOKEN);
        final SysUserService userService = SpringContextUtils.getBean(SysUserService.class);
        final SysUrlPermissionService urlPermissionService = SpringContextUtils.getBean(SysUrlPermissionService.class);
        String perm = null;
        if ((handler instanceof HandlerMethod)) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;

            Class<?> declaringClass = handlerMethod.getMethod().getDeclaringClass();
            if (!declaringClass.getPackage().getName().startsWith(INTERCEPT_PACKAGE_PREFIX)) {
                return true;
            }
            String url = getMappingUrl(handlerMethod);
            log.debug("url is {}", url);
            perm = urlPermissionService.getPathPerm(url);
            if (StringUtils.isBlank(perm)) {
                final RequiredPerm methodAnnotation = handlerMethod.getMethodAnnotation(RequiredPerm.class);
                if(methodAnnotation != null) {
                    perm = methodAnnotation.value();
                }
            }
            if (SysConstant.ANON.equals(perm)) {
                return true;
            }
        }
        if (SysConstant.DENY.equals(perm)) {
            BusinessException.throwEx(ExceptionEnum.NO_ACCESS);
        }
        if (SysConstant.LOGIN.equals(perm)) {
            if (StringUtils.isNotBlank(token)) {
                TokenUtils.isValid(token);
                final JWTToken jwtToken = TokenUtils.parseToken(token);
                UserUtils.set(jwtToken);
                return true;
            } else {
                BusinessException.throwEx(ExceptionEnum.UN_AUTH);
            }
        }

        TokenUtils.isValid(token);
        final JWTToken jwtToken = TokenUtils.parseToken(token);
        UserUtils.set(jwtToken);
        if (userService.hasPerm(perm)) {
            return true;
        } else {
            BusinessException.throwEx(ExceptionEnum.NO_ACCESS);
        }
        return false;
    }

    private String getMappingUrl(HandlerMethod handler) {
        RequestMappingHandlerMapping mapping = SpringContextUtils.getBean("requestMappingHandlerMapping", RequestMappingHandlerMapping.class);
        final String key = handler.toString();
        final RequestMappingInfo mappingInfo = requestMappingInfoMap.get(key);
        if (mappingInfo != null) {
            return StringUtils.join(mappingInfo.getPatternsCondition().getPatterns(), ",");
        }
        final Map<RequestMappingInfo, HandlerMethod> handlerMethods = mapping.getHandlerMethods();
        for (RequestMappingInfo requestMappingInfo : handlerMethods.keySet()) {
            final HandlerMethod handlerMethod = handlerMethods.get(requestMappingInfo);
            if (key.equals(handlerMethod.toString())) {
                requestMappingInfoMap.put(key, requestMappingInfo);
                return StringUtils.join(requestMappingInfo.getPatternsCondition().getPatterns(), ",");
            }
        }
        return null;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
        // 删除用户信息
        UserUtils.remove();
    }


}